Skip to content

feat: lower atan2, quotient and logical_xor in the native Expr compiler - #190

Merged
ausimian merged 1 commit into
feat/expr-compilerfrom
feat/expr-compiler-binary-ops
Jun 6, 2026
Merged

feat: lower atan2, quotient and logical_xor in the native Expr compiler#190
ausimian merged 1 commit into
feat/expr-compilerfrom
feat/expr-compiler-binary-ops

Conversation

@ausimian

@ausimian ausimian commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the binary/compare cluster on the Expr op-coverage checklist (#188). Three ops, two new IR opcodes, one composite clause — all bit-identical to the Evaluator.

  • atan2 — slots into @arith_binary as atan2: :arctan2. The shared handler casts both operands to out.type then emits the binary op (same as Emily.Backend's @renamed_arith_binary). New IR opcode arctan2 (111) dispatches to mx::arctan2.
  • quotient — joins @arith_binary as quotient: :floor_divide. Emily.Backend.quotient/3 is exactly floor_divide(astype(a, out.type), astype(b, out.type)), which is what the shared handler already does with the new floor_divide opcode (112 → mx::floor_divide).
  • logical_xor — no MLX primitive; the lowerer mirrors Emily.Backend.logical_xor/3 directly: (a != 0) != (b != 0), three not_equal emits over a pair of per-dtype scalar zeros, then coerce to {:u, 8}.

Probe (scripts/expr_op_coverage.exs) drops 18 → 15 misses; the binary/compare section of #188 is now empty.

Test plan

  • mix precommit clean (704 tests, 79 properties, 40 doctests, 0 failures — three new equivalence tests).
  • Op-coverage probe shows all three moved from MISS → OK.
  • New equivalence tests in compiler_equivalence_test.exs:
    • atan2 over the four quadrants and the axis boundaries (±0, ±pi, ±pi/2) so the result lands on every special case the eager Backend hits.
    • quotient over s32/s64 (signed, positive and negative dividends) and u8/u32 (including the u8 max boundary). Both paths route through mx::floor_divide so they match the Evaluator bit-for-bit.
    • logical_xor over the four truth-table corners for both f32 and s32, asserting the {:u, 8} predicate dtype after coerce.
  • Opcode parity test green — kOpcodeCount bumped 111 → 113 in lockstep with the Elixir @opcodes map and the C++ enum.

Follows the same pattern as #189.

Closes the binary/compare cluster on the Expr op-coverage checklist
(#188). Two new IR opcodes — `arctan2` (111) and `floor_divide` (112)
— plus a dedicated `logical_xor` lower clause.

- `atan2` slots into `@arith_binary` as `atan2: :arctan2`. The shared
  handler casts both operands to out.type and emits the binary op,
  matching Emily.Backend.atan2/3 (@renamed_arith_binary) bit-for-bit.
- `quotient` joins `@arith_binary` as `quotient: :floor_divide` —
  Emily.Backend.quotient/3 is exactly `floor_divide(astype(a, out.type),
  astype(b, out.type))`, which is what the shared handler does with the
  floor_divide opcode.
- `logical_xor` (no MLX primitive) gets a dedicated composite clause
  that mirrors Emily.Backend.logical_xor/3 — `(a != 0) != (b != 0)`
  using three not_equal emits over a pair of per-dtype scalar zeros,
  with the trailing coerce producing {:u, 8}.

Probe drops from 18 → 15 misses. Equivalence tests cover atan2 over
the four quadrants + axis boundaries, quotient over s32/s64/u8/u32,
and logical_xor over the four truth-table corners for both float and
integer dtypes (asserting the {:u, 8} predicate dtype after coerce).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant